package hardware;

import java.util.LinkedList;

public class Dispatch {
	static LinkedList<Instruction> instructions = new LinkedList<Instruction>();

	/**
	 * checks of QA and QB of reservation station are set to true.
	 * 
	 * @param rs
	 *            reservation station which we want to check.
	 * @return true if both registers are ready else false.
	 */
	public boolean checkReady(ReservationStation rs) {
		if (rs.isqA() && rs.isqB()) {
			return true;
		}
		return false;
	}

	/**
	 * put a certain instruction in a reservation station if any is free.
	 * 
	 * @param instruction
	 *            instruction that we need to add to the reservation station.
	 * @return returns true if there's a free reservation station.
	 */
	public static boolean putInReservationStation(Instruction instruction) {
		for (int i = 0; i < Processor.reservationStations.size(); i++) {
			if (Processor.reservationStations.get(i) != null
					&& !Processor.reservationStations.get(i).isFull()) {
				Processor.reservationStations.get(i).setIns(instruction);
				Processor.reservationStations.get(i).setFull(true);
//				String nameA = instruction.getRegB().name;
//				System.out.println("weza" + nameA);
//				//String nameB = "";
//				if (instruction.getRegC() != null)
//					nameB = instruction.getRegC().name;
				// check regA
				if(instruction.getRegB()!=null){
					if(instruction.getRegB().isReady()){
						Processor.reservationStations.get(i).setqA(true);
						Processor.reservationStations.get(i).setOperandOne(
								instruction.getRegB().value);
					}else{
						Processor.reservationStations.get(i).setqA(false);
					}
				}else{
					Processor.reservationStations.get(i).setqA(true);
				}
				
				//check regB
				if(instruction.getRegC()!=null){
					if(instruction.getRegC().isReady()){
						Processor.reservationStations.get(i).setqB(true);
						Processor.reservationStations.get(i).setOperandTwo(
								instruction.getRegB().value);
					}else{
						Processor.reservationStations.get(i).setqB(false);
					}
				}else{
					Processor.reservationStations.get(i).setqB(true);
				}
				
					if (!instruction.getType().equals(Instruction.STORE_WORD)
							&& !instruction.getType().equals(
									Instruction.BRANCH_IF_EQUAL)
							&& !instruction.getType().equals(Instruction.JUMP)
							&& !instruction.getType()
									.equals(Instruction.RETURN))
						instruction.getRegA().setValue(instruction.getID());// set
																			// source
																			// register
																			// value
																			// to
																			// instruction
																			// id.

					return true;
				//}
			}
		}
		return false;
	}

	/**
	 * adds instruction to ROB 
	 * @param instruction
	 *                    the instruction we need to add to ROB.
	 */
	public static void addToROB(Instruction instruction, Register logical) {
		Processor.rob.add(new ROBEntry(logical, instruction.getType(), instruction.getID()));
	}

	/**
	 * checks if the registers are ready  and there is free functional unit to be reserved. 
	 * @param currentTime
	 *                   the current time at which the operation is performed.
	 */
	public static void dispatch(long currentTime) { // Dispatch for the old
													// instructions in the list
													// of dispatch and moves
													// them to the next
													// stage(Issue).
		Instruction ins;
		ReservationStation res;
		for (int i = 0; i < instructions.size(); i++) {
			ins = instructions.get(i);
			res = Processor.checkReservationStation(ins);
			if (Issue.instructions.size() < Processor.m) {
				if (res!=null && res.isqA() && res.isqB()) {
					if (Processor.reserveFunctionalUnit(ins)) {
						ins.setDispEndTime(currentTime);
						Issue.issueNew(ins, currentTime + 1);
						instructions.remove(i);
						i--;
					}
				}
			}
		}
		for(int i =0;i<instructions.size();i++){
			updateReservationStations(instructions.get(i));
		}
	}

	private static void updateReservationStations(Instruction instruction) {
		ReservationStation reservationStation = Processor.getReservationStation(instruction);
		if(!reservationStation.isqA()){
		if(instruction.getRegB().isReady()){
			reservationStation.setqA(true);
		}
		}
		
		if(!reservationStation.isqB()){
			if(instruction.getRegC().isReady()){
				reservationStation.setqB(true);
			}
			}
	}

	/**
	 * adds instruction to list of instructions in Dispatch.
	 * @param instruction
	 *                   the instruction we need to add to Dispatch stage.
	 * @param currentTime
	 *                   the current time at which the operation is performed.
	 */
	public static void addInstructionToDispatchStage(Instruction instruction,
			Long currentTime) {
		instruction.setDispStartTime(currentTime);
		instructions.add(instruction);
	}

	/**
	 * Handles the new instructions entering dispatch
	 * @param instruction
	 *                   the instruction coming to dispatch.
	 * @param currentTime
	 *                   the current time at which the operation is performed.
	 */
	public static void dispatchNew(Instruction instruction, Register logical, long currentTime) {
		addInstructionToDispatchStage(instruction, currentTime);
		putInReservationStation(instruction);
		addToROB(instruction, logical);
		
	}

}
